/**
 * Copyright (C) 2012-2017 Iordan Iordanov
 * Copyright (C) 2010 Michael A. MacDonald
 * <p>
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * <p>
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * <p>
 * You should have received a copy of the GNU General Public License
 * along with this software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 * USA.
 */

//
// CanvasView is the Activity for showing VNC Desktop.
//
package com.iiordanov.bVNC;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.os.Vibrator;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Toast;

import com.iiordanov.android.bc.BCFactory;
import com.iiordanov.bVNC.dialogs.CustomDialog;
import com.iiordanov.bVNC.dialogs.EnterTextDialog;
import com.iiordanov.bVNC.dialogs.MetaKeyDialog;
import com.iiordanov.bVNC.event.bVNCEvent;
import com.iiordanov.bVNC.input.InputHandler;
import com.iiordanov.bVNC.input.InputHandlerDirectDragPan;
import com.iiordanov.bVNC.input.InputHandlerDirectSwipePan;
import com.iiordanov.bVNC.input.InputHandlerSingleHanded;
import com.iiordanov.bVNC.input.InputHandlerTouchpad;
import com.iiordanov.bVNC.input.Panner;
import com.iiordanov.bVNC.input.RemoteKeyboard;
import com.iiordanov.bVNC.input.RemotePointer;
import com.undatech.opaque.Connection;
import com.undatech.opaque.dialogs.GetTextFragment;
import com.undatech.opaque.dialogs.SelectTextElementFragment;
import com.undatech.opaque.ConnectionSettings;
import com.undatech.opaque.MessageDialogs;
import com.undatech.opaque.OpaqueHandler;
import com.undatech.opaque.RemoteClientLibConstants;
import com.undatech.opaque.util.FileUtils;
import com.undatech.remoteClientUi.R;
import com.yw.game.floatmenu.FloatItem;
import com.yw.game.floatmenu.FloatLogoMenu;
import com.yw.game.floatmenu.FloatMenuView;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;

public class RemoteCanvasActivity extends Activity implements OnKeyListener,
        SelectTextElementFragment.OnFragmentDismissedListener,
        GetTextFragment.OnFragmentDismissedListener {

    private final static String TAG = "RemoteCanvasActivity";

    InputHandler inputHandler;
    private Vibrator myVibrator;

    private RemoteCanvas canvas;

    private MenuItem[] inputModeMenuItems;
    private MenuItem[] scalingModeMenuItems;
    private InputHandler inputModeHandlers[];
    private Connection connection;
    static final int[] inputModeIds = {R.id.itemInputTouchpad,
            R.id.itemInputTouchPanZoomMouse,
            R.id.itemInputDragPanZoomMouse,
            R.id.itemInputSingleHanded};
    private static final int scalingModeIds[] = {R.id.itemZoomable, R.id.itemFitToScreen,
            R.id.itemOneToOne};

    static final Map<Integer, String> inputModeMap;

    static {
        Map<Integer, String> temp = new HashMap<>();
        temp.put(R.id.itemInputTouchpad, InputHandlerTouchpad.ID);
        temp.put(R.id.itemInputDragPanZoomMouse, InputHandlerDirectDragPan.ID);
        temp.put(R.id.itemInputTouchPanZoomMouse, InputHandlerDirectSwipePan.ID);
        temp.put(R.id.itemInputSingleHanded, InputHandlerSingleHanded.ID);
        inputModeMap = Collections.unmodifiableMap(temp);
    }

    Panner panner;
    Handler handler;

    RelativeLayout layoutKeys;
    LinearLayout layoutArrowKeys;
    ImageButton keyCtrl;
    boolean keyCtrlToggled;
    ImageButton keySuper;
    boolean keySuperToggled;
    ImageButton keyAlt;
    boolean keyAltToggled;
    ImageButton keyTab;
    ImageButton keyEsc;
    ImageButton keyShift;
    boolean keyShiftToggled;
    ImageButton keyUp;
    ImageButton keyDown;
    ImageButton keyLeft;
    ImageButton keyRight;
    boolean hardKeyboardExtended;
    boolean extraKeysHidden = false;
    int prevBottomOffset = 0;
    volatile boolean softKeyboardUp;
    Toolbar toolbar;
    RemotePointer pointer;

    /**
     * This runnable enables immersive mode.
     */
    private Runnable immersiveEnabler = new Runnable() {
        public void run() {
            try {
                if (Utils.querySharedPreferenceBoolean(RemoteCanvasActivity.this,
                        Constants.disableImmersiveTag)) {
                    return;
                }

                if (Constants.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
                    canvas.setSystemUiVisibility(
                            View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                                    | View.SYSTEM_UI_FLAG_FULLSCREEN
                                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
                }

            } catch (Exception e) {
            }
        }
    };
    private FloatLogoMenu mFloatMenu;
    private boolean exitFlag;
    public String dToken;
    public int dId;
    private String mIp;

    /**
     * Enables sticky immersive mode if supported.
     */
    private void enableImmersive() {
        handler.removeCallbacks(immersiveEnabler);
        handler.postDelayed(immersiveEnabler, 200);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            enableImmersive();
        }
    }

    public void setRemoteCanvasTop(int width, int height) {
        int screenWidth = Utils.getScreenWidth();
        int screenHeight = Utils.getScreenHeight();
        int h = height * screenWidth / width;
        int dh = screenHeight - h;
        int top = dh / 2;
        FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) canvas.getLayoutParams();
        Log.e("XA", "H = " + h + "  width = " + width + " height = " + height + " screenWidth =  " + screenWidth + "  screenHeight = " + screenHeight + "\n"
                + " layoutParams.bottomMargin = " + layoutParams.bottomMargin);
        layoutParams.setMargins(0, top, 0, layoutParams.bottomMargin);
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        // TODO: Implement left-icon
        //requestWindowFeature(Window.FEATURE_LEFT_ICON);
        //setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, R.drawable.icon);
        EventBus.getDefault().register(this);

//        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
//                WindowManager.LayoutParams.FLAG_FULLSCREEN);

//        Utils.showMenu(this);

        setContentView(R.layout.canvas);

        canvas = (RemoteCanvas) findViewById(R.id.canvas);

        if (android.os.Build.VERSION.SDK_INT >= 9) {
            android.os.StrictMode.ThreadPolicy policy = new android.os.StrictMode.ThreadPolicy.Builder().permitAll().build();
            android.os.StrictMode.setThreadPolicy(policy);
        }

        View decorView = getWindow().getDecorView();
        decorView.setOnSystemUiVisibilityChangeListener
                (new View.OnSystemUiVisibilityChangeListener() {
                    @Override
                    public void onSystemUiVisibilityChange(int visibility) {
                        try {
                            correctAfterRotation();
                        } catch (Exception e) {
                            //e.printStackTrace();
                        }
                        //handler.postDelayed(rotationCorrector, 300);
                    }
                });

        Runnable setModes = new Runnable() {
            public void run() {
                try {
                    setModes();
                } catch (NullPointerException e) {
                }
            }
        };
        //初始化界面
        initialize(setModes);
        if (connection != null && connection.isReadyForConnection()) {
            continueConnecting();
        }

        ArrayList<FloatItem> floatItems = new ArrayList<>();
        floatItems.add(new FloatItem("切换设备", R.color.white, R.color.white, BitmapFactory.decodeResource(this.getResources(), R.drawable.ic_xuanfu_qiehuan)));
        floatItems.add(new FloatItem("退出", R.color.white, R.color.white, BitmapFactory.decodeResource(this.getResources(), R.drawable.ic_xuanfu_exit)));

        mFloatMenu = new FloatLogoMenu.Builder()
                .withActivity(this)
                //  .withContext(mActivity.getApplication())//这个在7.0（包括7.0）以上以及大部分7.0以下的国产手机上需要用户授权，需要搭配<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
                .logo(BitmapFactory.decodeResource(getResources(), R.drawable.ic_mode_major_wifi_green))
                .drawCicleMenuBg(true)
//                .backMenuColor(0xffe4e3e1)
                .setBgDrawable(this.getResources().getDrawable(R.drawable.bg_common_root))
                //这个背景色需要和logo的背景色一致
                .setFloatItems(floatItems)
                .defaultLocation(FloatLogoMenu.RIGHT)
                .drawRedPointNum(false)
                .showWithListener(new FloatMenuView.OnMenuClickListener() {
                    @Override
                    public void onItemClick(int position, String title) {
                        if (position == 0) {
                            EventBus.getDefault().post(new bVNCEvent.ShowDevicesListEvent(dId));
                        } else if (position == 1) {
                            //退出
                            CustomDialog.Builder builder = new CustomDialog.Builder(RemoteCanvasActivity.this);
                            builder.setMessage("是否退出云手机？");
                            builder.setTitle("提示");
                            builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.dismiss();
                                    disConnect();
                                }
                            });

                            builder.setNegativeButton("取消",
                                    new android.content.DialogInterface.OnClickListener() {
                                        public void onClick(DialogInterface dialog, int which) {
                                            dialog.dismiss();
                                        }
                                    });

                            builder.create().show();

                        }
                    }

                    @Override
                    public void dismiss() {

                    }
                });
        mFloatMenu.show();
        new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        while (!exitFlag) {
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            final String delay = getDelay();

                            try {
                                int d = Integer.parseInt(delay);
                                if (d <= 60) {
                                    if (lastDrawableId != R.drawable.ic_mode_major_wifi_green) {
                                        mFloatMenu.setFloatLogoBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.ic_mode_major_wifi_green));
                                        lastDrawableId = R.drawable.ic_mode_major_wifi_green;
                                    }
                                } else if (d > 60 && d < 120) {
                                    if (lastDrawableId != R.drawable.ic_mode_major_wifi_orange) {
                                        mFloatMenu.setFloatLogoBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.ic_mode_major_wifi_orange));
                                        lastDrawableId = R.drawable.ic_mode_major_wifi_orange;
                                    }
                                } else {
                                    if (lastDrawableId != R.drawable.ic_mode_major_wifi_red) {
                                        mFloatMenu.setFloatLogoBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.ic_mode_major_wifi_red));
                                        lastDrawableId = R.drawable.ic_mode_major_wifi_red;
                                    }
                                }
                            } catch (Exception e) {
                                Log.e("XA", Log.getStackTraceString(e));
                            }

                        }
                    }
                }
        ).start();
    }

    public int lastDrawableId;

    public String getDelay() {
        String delay = "";
        Process p = null;
        try {
            p = Runtime.getRuntime().exec("/system/bin/ping -c 1 " + mIp);
            BufferedReader buf = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String str = "";
            while ((str = buf.readLine()) != null) {
//                Log.e("XA", "str = " + str);
                if (str.contains("avg")) {
                    int i = str.indexOf("/", 20);
                    int j = str.indexOf(".", i);
                    Log.e("XA", "延迟:" + str.substring(i + 1, j));
                    delay = str.substring(i + 1, j);
                }
            }
//            Log.e("XA", "退出！！");
        } catch (IOException e) {
            Log.e("XA", Log.getStackTraceString(e));
        }
        return delay;

    }

    private void disConnect() {
        jieping();
        canvas.closeConnection();
        EventBus.getDefault().post(new bVNCEvent.UInotifyEvent());
        finish();
    }

    private void disConnectAndOpen(String ip, int port, String token, int id) {
        mFloatMenu.destroyFloat();
        disConnect();
        ConnectionBean connectionBean = new ConnectionBean(this);
        connectionBean.setAddress(ip);
        connectionBean.setPort(port);
        connectionBean.setToken(token);
        connectionBean.set_Id(id);
        Intent intent = new Intent(this, RemoteCanvasActivity.class);
        intent.putExtra(Utils.getConnectionString(this), connectionBean.Gen_getValues());
        startActivity(intent);
    }

    @SuppressLint("SourceLockedOrientationActivity")
    void initialize(final Runnable setModes) {
        handler = new Handler();

//        if (Utils.querySharedPreferenceBoolean(this, Constants.keepScreenOnTag))
//            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
//
//        if (Utils.querySharedPreferenceBoolean(this, Constants.forceLandscapeTag))
//            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);

        Intent i = getIntent();
        connection = null;

        Uri data = i.getData();
        dToken = (String) ((ContentValues) (i.getParcelableExtra("com.zzkj.yunphone.CONNECTION"))).get("_token");
        mIp = (String) ((ContentValues) (i.getParcelableExtra("com.zzkj.yunphone.CONNECTION"))).get("ADDRESS");
        String id = (String) ((ContentValues) (i.getParcelableExtra("com.zzkj.yunphone.CONNECTION"))).get("_id");
        dId = Integer.parseInt(id);
        boolean isSupportedScheme = false;
        if (data != null) {
            String s = data.getScheme();
            isSupportedScheme = s.equals("rdp") || s.equals("spice") || s.equals("vnc");
        }

        if (isSupportedScheme || !Utils.isNullOrEmptry(i.getType())) {
            if (isMasterPasswordEnabled()) {
                Utils.showFatalErrorMessage(this, getResources().getString(R.string.master_password_error_intents_not_supported));
                return;
            }
            connection = ConnectionBean.createLoadFromUri(data, this);
            String host = data.getHost();
            if (!host.startsWith(Utils.getConnectionString(this))) {
                connection.parseFromUri(data);
            }

            if (connection.isReadyToBeSaved()) {
                connection.saveAndWriteRecent(false, this);
            }
            // we need to save the connection to display the loading screen, so otherwise we should exit
            if (!connection.isReadyForConnection()) {
                Toast.makeText(this, getString(R.string.error_uri_noinfo_nosave), Toast.LENGTH_LONG).show();
                ;
                if (!connection.isReadyToBeSaved()) {
                    Log.i(TAG, "Exiting - Insufficent information to connect and connection was not saved.");
                } else {
                    Log.i(TAG, "Insufficent information to connect, showing connection dialog.");
                    // launch appropriate activity
                    Class cls = bVNC.class;
                    if (Utils.isRdp(getPackageName())) {
                        cls = aRDP.class;
                    } else if (Utils.isSpice(getPackageName())) {
                        cls = aSPICE.class;
                    }
                    Intent bVncIntent = new Intent(this, cls);
                    startActivity(bVncIntent);
                }
                finish();
                return;
            }
        } else {
            connection = new ConnectionBean(this);
            Bundle extras = i.getExtras();

            if (extras != null) {
                connection.populateFromContentValues((ContentValues) extras.getParcelable(Utils.getConnectionString(this)));
            }

            // Parse a HOST:PORT entry but only if not ipv6 address
            String host = connection.getAddress();
            if (!Utils.isValidIpv6Address(host) && host.indexOf(':') > -1) {
                String p = host.substring(host.indexOf(':') + 1);
                try {
                    int parsedPort = Integer.parseInt(p);
                    connection.setPort(parsedPort);
                    connection.setAddress(host.substring(0, host.indexOf(':')));
                } catch (Exception e) {
                }
            }

            if (connection.getPort() == 0)
                connection.setPort(Constants.DEFAULT_PROTOCOL_PORT);

            if (connection.getSshPort() == 0)
                connection.setSshPort(Constants.DEFAULT_SSH_PORT);
        }
        pointer = canvas.initializeCanvas(connection, setModes);
        AbstractScaling.getById(R.id.itemFitToScreen).setScaleTypeForActivity(this);
    }

    @SuppressLint("SourceLockedOrientationActivity")
    private void initializeOpaque(final Runnable setModes) {
        setVolumeControlStream(AudioManager.STREAM_MUSIC);
        Intent i = getIntent();
        String vvFileName = retrieveVvFileFromIntent(i);
        if (vvFileName == null) {
            android.util.Log.d(TAG, "Initializing session from connection settings.");
            connection = (ConnectionSettings) i.getSerializableExtra("com.undatech.opaque.ConnectionSettings");
            handler = new OpaqueHandler(this, canvas, connection);
            pointer = canvas.init(connection, handler, setModes);

            if (connection.getConnectionTypeString().equals(getResources().getString(R.string.connection_type_pve))) {
                canvas.startPve();
            } else {
                canvas.startOvirt();
            }
        } else {
            android.util.Log.d(TAG, "Initializing session from vv file: " + vvFileName);
            File f = new File(vvFileName);
            if (!f.exists()) {
                // Quit with an error if the file does not exist.
                MessageDialogs.displayMessageAndFinish(this, R.string.vv_file_not_found, R.string.error_dialog_title);
                return;
            }
            connection = new ConnectionSettings(RemoteClientLibConstants.DEFAULT_SETTINGS_FILE);
            connection.load(getApplicationContext());
            handler = new OpaqueHandler(this, canvas, connection);
            pointer = canvas.init(connection, handler, setModes);
            canvas.startFromVvFile(vvFileName);
        }
    }

    void continueConnecting() {
        android.util.Log.d(TAG, "continueConnecting");
        // Initialize and define actions for on-screen keys.
//        initializeOnScreenKeys();

        canvas.setOnKeyListener(this);
        canvas.setFocusableInTouchMode(true);
        canvas.setDrawingCacheEnabled(false);

        // This code detects when the soft keyboard is up and sets an appropriate visibleHeight in vncCanvas.
        // When the keyboard is gone, it resets visibleHeight and pans zero distance to prevent us from being
        // below the desktop image (if we scrolled all the way down when the keyboard was up).
        // TODO: Move this into a separate thread, and post the visibility changes to the handler.
        //       to avoid occupying the UI thread with this.
        final View rootView = ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0);
        rootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (canvas == null) {
                    return;
                }

                Rect r = new Rect();

                rootView.getWindowVisibleDisplayFrame(r);

                // To avoid setting the visible height to a wrong value after an screen unlock event
                // (when r.bottom holds the width of the screen rather than the height due to a rotation)
                // we make sure r.top is zero (i.e. there is no notification bar and we are in full-screen mode)
                // It's a bit of a hack.
                if (r.top == 0) {
                    if (canvas.myDrawable != null) {
                        canvas.setVisibleDesktopHeight(r.bottom);
                        canvas.relativePan(0, 0);
                    }
                }

                // Enable/show the toolbar if the keyboard is gone, and disable/hide otherwise.
                // We detect the keyboard if more than 19% of the screen is covered.
                int topBottomOffset = 0;
                int leftRightOffset = 0;
                // Use the visible display frame of the decor view to compute notch dimensions.
                Rect re = new Rect();
                getWindow().getDecorView().getWindowVisibleDisplayFrame(re);
                int rootViewHeight = rootView.getHeight();
                int rootViewWidth = rootView.getWidth();
                if (r.bottom > rootViewHeight * 0.81) {
                    softKeyboardUp = false;
                    topBottomOffset = rootViewHeight - r.bottom + re.top;
                    leftRightOffset = rootViewWidth - r.right + re.left;

                    // Soft Kbd gone, shift the meta keys and arrows down.
                    if (layoutKeys != null) {
                        layoutKeys.offsetTopAndBottom(topBottomOffset);
                        layoutArrowKeys.offsetLeftAndRight(leftRightOffset);
                        if (prevBottomOffset != topBottomOffset) {
                            prevBottomOffset = topBottomOffset;
                            setExtraKeysVisibility(View.GONE, false);
                            canvas.invalidate();
                        }
                    }
                } else {
                    softKeyboardUp = true;
                    topBottomOffset = r.bottom - rootViewHeight - re.top;
                    leftRightOffset = r.right - rootViewWidth - re.left;

                    //  Soft Kbd up, shift the meta keys and arrows up.
                    if (layoutKeys != null) {
                        layoutKeys.offsetTopAndBottom(topBottomOffset);
                        layoutArrowKeys.offsetLeftAndRight(leftRightOffset);
                        if (extraKeysHidden) {
                            extraKeysHidden = !extraKeysHidden;
                            setExtraKeysVisibility(View.VISIBLE, true);
                            extraKeysHidden = !extraKeysHidden;
                            setExtraKeysVisibility(View.GONE, false);
                        } else {
                            extraKeysHidden = !extraKeysHidden;
                            setExtraKeysVisibility(View.GONE, false);
                            extraKeysHidden = !extraKeysHidden;
                            setExtraKeysVisibility(View.VISIBLE, true);
                        }
                        canvas.invalidate();
                        prevBottomOffset = topBottomOffset;
                    }
                }
            }
        });

        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.WRAP_CONTENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);

        if (Utils.querySharedPreferenceBoolean(this, Constants.leftHandedModeTag)) {
            params.gravity = Gravity.CENTER | Gravity.LEFT;
        } else {
            params.gravity = Gravity.CENTER | Gravity.RIGHT;
        }

        panner = new Panner(this, canvas.handler);

        inputHandler = getInputHandlerById(R.id.itemInputTouchPanZoomMouse);

//        toolbar = (Toolbar) findViewById(R.id.toolbar);
//        toolbar.setTitle("");
//        toolbar.getBackground().setAlpha(64);
//        toolbar.setLayoutParams(params);
//        setSupportActionBar(toolbar);
//        showToolbar();
    }

    /**
     * Retrieves a vv file from the intent if possible and returns the path to it.
     *
     * @param i
     * @return the vv file name or NULL if no file was discovered.
     */
    private String retrieveVvFileFromIntent(Intent i) {
        final Uri data = i.getData();
        String vvFileName = null;
        final String tempVvFile = getFilesDir() + "/tempfile.vv";
        int msgId = 0;

        android.util.Log.d(TAG, "Got intent: " + i.toString());

        if (data != null) {
            android.util.Log.d(TAG, "Got data: " + data.toString());
            final String dataString = data.toString();
            if (dataString.startsWith("http")) {
                android.util.Log.d(TAG, "Intent is with http scheme.");
                msgId = R.string.error_failed_to_download_vv_http;
                FileUtils.deleteFile(tempVvFile);

                // Spin up a thread to grab the file over the network.
                Thread t = new Thread() {
                    @Override
                    public void run() {
                        try {
                            // Download the file and write it out.
                            URL url = new URL(data.toString());
                            File file = new File(tempVvFile);

                            URLConnection ucon = url.openConnection();
                            FileUtils.outputToFile(ucon.getInputStream(), new File(tempVvFile));

                            synchronized (RemoteCanvasActivity.this) {
                                RemoteCanvasActivity.this.notify();
                            }
                        } catch (IOException e) {
                            int what = RemoteClientLibConstants.VV_OVER_HTTP_FAILURE;
                            if (dataString.startsWith("https")) {
                                what = RemoteClientLibConstants.VV_OVER_HTTPS_FAILURE;
                            }
                            // Quit with an error we could not download the .vv file.
                            handler.sendEmptyMessage(what);
                        }
                    }
                };
                t.start();

                synchronized (this) {
                    try {
                        this.wait(RemoteClientLibConstants.VV_GET_FILE_TIMEOUT);
                    } catch (InterruptedException e) {
                        vvFileName = null;
                        e.printStackTrace();
                    }
                    vvFileName = tempVvFile;
                }
            } else if (dataString.startsWith("file")) {
                android.util.Log.d(TAG, "Intent is with file scheme.");
                msgId = R.string.error_failed_to_obtain_vv_file;
                vvFileName = data.getPath();
            } else if (dataString.startsWith("content")) {
                android.util.Log.d(TAG, "Intent is with content scheme.");
                msgId = R.string.error_failed_to_obtain_vv_content;
                FileUtils.deleteFile(tempVvFile);

                try {
                    FileUtils.outputToFile(getContentResolver().openInputStream(data), new File(tempVvFile));
                    vvFileName = tempVvFile;
                } catch (IOException e) {
                    android.util.Log.e(TAG, "Could not write temp file: IOException.");
                    e.printStackTrace();
                } catch (SecurityException e) {
                    android.util.Log.e(TAG, "Could not write temp file: SecurityException.");
                    e.printStackTrace();
                }
            }

            // Check if we were successful in obtaining a file and put up an error dialog if not.
            if (dataString.startsWith("http") || dataString.startsWith("file") || dataString.startsWith("content")) {
                if (vvFileName == null)
                    MessageDialogs.displayMessageAndFinish(this, msgId, R.string.error_dialog_title);
            }
            android.util.Log.d(TAG, "Got filename: " + vvFileName);
        }

        return vvFileName;
    }


    /**
     * Resets the state and image of the on-screen keys.
     */
    private void resetOnScreenKeys(int keyCode) {
        // Do not reset on-screen keys if keycode is SHIFT.
        switch (keyCode) {
            case KeyEvent.KEYCODE_SHIFT_LEFT:
            case KeyEvent.KEYCODE_SHIFT_RIGHT:
                return;
        }
        if (!keyCtrlToggled) {
            keyCtrl.setImageResource(R.drawable.ctrloff);
            canvas.getKeyboard().onScreenCtrlOff();
        }
        if (!keyAltToggled) {
            keyAlt.setImageResource(R.drawable.altoff);
            canvas.getKeyboard().onScreenAltOff();
        }
        if (!keySuperToggled) {
            keySuper.setImageResource(R.drawable.superoff);
            canvas.getKeyboard().onScreenSuperOff();
        }
        if (!keyShiftToggled) {
            keyShift.setImageResource(R.drawable.shiftoff);
            canvas.getKeyboard().onScreenShiftOff();
        }
    }


    /**
     * Sets the visibility of the extra keys appropriately.
     */
    private void setExtraKeysVisibility(int visibility, boolean forceVisible) {
        Configuration config = getResources().getConfiguration();
        //Log.e(TAG, "Hardware kbd hidden: " + Integer.toString(config.hardKeyboardHidden));
        //Log.e(TAG, "Any keyboard hidden: " + Integer.toString(config.keyboardHidden));
        //Log.e(TAG, "Keyboard type: " + Integer.toString(config.keyboard));

        boolean makeVisible = forceVisible;
        if (config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO)
            makeVisible = true;

        if (!extraKeysHidden && makeVisible &&
                connection.getExtraKeysToggleType() == RemoteClientLibConstants.EXTRA_KEYS_ON) {
            layoutKeys.setVisibility(View.VISIBLE);
            layoutKeys.invalidate();
            return;
        }

        if (visibility == View.GONE) {
            layoutKeys.setVisibility(View.GONE);
            layoutKeys.invalidate();
        }
    }

    /*
     * TODO: REMOVE THIS AS SOON AS POSSIBLE.
     * onPause: This is an ugly hack for the Playbook, because the Playbook hides the keyboard upon unlock.
     * This causes the visible height to remain less, as if the soft keyboard is still up. This hack must go
     * away as soon as the Playbook doesn't need it anymore.
     */
    @Override
    protected void onPause() {
        super.onPause();
        try {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(canvas.getWindowToken(), 0);
        } catch (NullPointerException e) {
        }
    }

    /*
     * TODO: REMOVE THIS AS SOON AS POSSIBLE.
     * onResume: This is an ugly hack for the Playbook which hides the keyboard upon unlock. This causes the visible
     * height to remain less, as if the soft keyboard is still up. This hack must go away as soon
     * as the Playbook doesn't need it anymore.
     */
    @Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "onResume called.");
        try {
            canvas.postInvalidateDelayed(600);
        } catch (NullPointerException e) {
        }
    }

    /**
     * Set modes on start to match what is specified in the ConnectionBean;
     * color mode (already done) scaling, input mode
     */
    void setModes() {
        InputHandler handler = getInputHandlerByName(connection.getInputMode());
        AbstractScaling.getByScaleType(connection.getScaleMode()).setScaleTypeForActivity(this);
        this.inputHandler = handler;
        //showPanningState(false);
    }

    /*
     * (non-Javadoc)
     *
     * @see android.app.Activity#onCreateDialog(int)
     */
    @Override
    protected Dialog onCreateDialog(int id) {
        if (id == R.layout.entertext) {
            return new EnterTextDialog(this);
        } else if (id == R.id.itemHelpInputMode) {
            return createHelpDialog();
        }

        // Default to meta key dialog
        return new MetaKeyDialog(this);
    }

    /**
     * Creates the help dialog for this activity.
     */
    private Dialog createHelpDialog() {
        AlertDialog.Builder adb = new AlertDialog.Builder(this)
                .setMessage(R.string.input_mode_help_text)
                .setPositiveButton(R.string.close,
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog,
                                                int whichButton) {
                                // We don't have to do anything.
                            }
                        });
        Dialog d = adb.setView(new ListView(this)).create();
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
        lp.copyFrom(d.getWindow().getAttributes());
        lp.width = WindowManager.LayoutParams.MATCH_PARENT;
        lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
        d.show();
        d.getWindow().setAttributes(lp);
        return d;
    }

    /*
     * (non-Javadoc)
     *
     * @see android.app.Activity#onPrepareDialog(int, android.app.Dialog)
     */
    @SuppressWarnings("deprecation")
    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
        super.onPrepareDialog(id, dialog);
        if (dialog instanceof ConnectionSettable)
            ((ConnectionSettable) dialog).setConnection(connection);
    }

    /**
     * This runnable fixes things up after a rotation.
     */
    private Runnable rotationCorrector = new Runnable() {
        public void run() {
            try {
                correctAfterRotation();
            } catch (Exception e) {
            }
        }
    };

    /**
     * This function is called by the rotationCorrector runnable
     * to fix things up after a rotation.
     */
    private void correctAfterRotation() throws Exception {
        canvas.waitUntilInflated();
        // Its quite common to see NullPointerExceptions here when this function is called
        // at the point of disconnection. Hence, we catch and ignore the error.
        float oldScale = canvas.canvasZoomer.getZoomFactor();
        int x = canvas.absoluteXPosition;
        int y = canvas.absoluteYPosition;
        canvas.canvasZoomer.setScaleTypeForActivity(RemoteCanvasActivity.this);
        float newScale = canvas.canvasZoomer.getZoomFactor();
        canvas.canvasZoomer.changeZoom(this, oldScale / newScale, 0, 0);
        newScale = canvas.canvasZoomer.getZoomFactor();
        if (newScale <= oldScale &&
                canvas.canvasZoomer.getScaleType() != ImageView.ScaleType.FIT_CENTER) {
            canvas.absoluteXPosition = x;
            canvas.absoluteYPosition = y;
            canvas.resetScroll();
        }
        if (canvas.isVnc && connection.getRdpResType() == Constants.VNC_GEOM_SELECT_AUTOMATIC) {
            canvas.rfbconn.requestResolution(canvas.getWidth(), canvas.getHeight());
        }
    }


    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        enableImmersive();
        try {
            setExtraKeysVisibility(View.GONE, false);

            // Correct a few times just in case. There is no visual effect.
            handler.postDelayed(rotationCorrector, 300);
        } catch (NullPointerException e) {
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        try {
            canvas.postInvalidateDelayed(800);
        } catch (NullPointerException e) {
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        try {
            canvas.postInvalidateDelayed(1000);
        } catch (NullPointerException e) {
        }
    }

    @Override
    public void onPanelClosed(int featureId, Menu menu) {
        super.onPanelClosed(featureId, menu);
        showToolbar();
        enableImmersive();
    }


    /**
     * Change the input mode sub-menu to reflect change in scaling
     */
    void updateInputMenu() {
        try {
            for (MenuItem item : inputModeMenuItems) {
                item.setEnabled(canvas.canvasZoomer.isValidInputMode(item.getItemId()));
                if (getInputHandlerById(item.getItemId()) == inputHandler)
                    item.setChecked(true);
            }
        } catch (NullPointerException e) {
        }
    }

    /**
     * If id represents an input handler, return that; otherwise return null
     *
     * @param id
     * @return
     */
    InputHandler getInputHandlerById(int id) {
        myVibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);

        if (inputModeHandlers == null) {
            inputModeHandlers = new InputHandler[inputModeIds.length];
        }
        for (int i = 0; i < inputModeIds.length; ++i) {
            if (inputModeIds[i] == id) {
                if (inputModeHandlers[i] == null) {
                    if (id == R.id.itemInputTouchPanZoomMouse) {
                        inputModeHandlers[i] = new InputHandlerDirectSwipePan(this, canvas, pointer, myVibrator);
                    } else if (id == R.id.itemInputDragPanZoomMouse) {
                        inputModeHandlers[i] = new InputHandlerDirectDragPan(this, canvas, pointer, myVibrator);
                    } else if (id == R.id.itemInputTouchpad) {
                        inputModeHandlers[i] = new InputHandlerTouchpad(this, canvas, pointer, myVibrator);
                    } else if (id == R.id.itemInputSingleHanded) {
                        inputModeHandlers[i] = new InputHandlerSingleHanded(this, canvas, pointer, myVibrator);
                    } else {
                        throw new IllegalStateException("Unexpected value: " + id);
                    }
                }
                return inputModeHandlers[i];
            }
        }
        return null;
    }

    InputHandler getInputHandlerByName(String name) {
        Log.e(TAG, "getInputHandlerByName name = " + name);
        InputHandler result = null;
        for (int id : inputModeIds) {
            InputHandler handler = getInputHandlerById(id);
            if (handler.getId().equals(name)) {
                result = handler;
                break;
            }
        }
        if (result == null) {
            result = getInputHandlerById(R.id.itemInputTouchPanZoomMouse);
        }
        return result;
    }

    int getModeIdFromHandler(InputHandler handler) {
        for (int id : inputModeIds) {
            if (handler == getInputHandlerById(id))
                return id;
        }
        return R.id.itemInputTouchPanZoomMouse;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        RemoteKeyboard k = canvas.getKeyboard();
        if (k != null) {
            k.setAfterMenu(true);
        }
        int itemId = item.getItemId();
        if (itemId == R.id.itemInfo) {
            canvas.showConnectionInfo();
            return true;
        } else if (itemId == R.id.itemSpecialKeys) {
            showDialog(R.layout.metakey);
            return true;
        } else if (itemId == R.id.itemColorMode) {
            selectColorModel();
            return true;
            // Following sets one of the scaling options
        } else if (itemId == R.id.itemZoomable || itemId == R.id.itemOneToOne || itemId == R.id.itemFitToScreen) {
            AbstractScaling.getById(item.getItemId()).setScaleTypeForActivity(this);
            item.setChecked(true);
            showPanningState(false);
            return true;
        } else if (itemId == R.id.itemCenterMouse) {
            canvas.getPointer().movePointer(canvas.absoluteXPosition + canvas.getVisibleDesktopWidth() / 2,
                    canvas.absoluteYPosition + canvas.getVisibleDesktopHeight() / 2);
            return true;
        } else if (itemId == R.id.itemDisconnect) {
            jieping();
            canvas.closeConnection();
            finish();
            return true;
        } else if (itemId == R.id.itemEnterText) {
            showDialog(R.layout.entertext);
            return true;
        } else if (itemId == R.id.itemCtrlAltDel) {
            canvas.getKeyboard().sendMetaKey(MetaKeyBean.keyCtrlAltDel);
            return true;
        } else if (itemId == R.id.itemSendKeyAgain) {
            sendSpecialKeyAgain();
            return true;
            // Disabling Manual/Wiki Menu item as the original does not correspond to this project anymore.
            //case R.id.itemOpenDoc:
            //    Utils.showDocumentation(this);
            //    return true;
        } else if (itemId == R.id.itemExtraKeys) {
            if (connection.getExtraKeysToggleType() == Constants.EXTRA_KEYS_ON) {
                connection.setExtraKeysToggleType(Constants.EXTRA_KEYS_OFF);
                item.setTitle(R.string.extra_keys_enable);
                setExtraKeysVisibility(View.GONE, false);
            } else {
                connection.setExtraKeysToggleType(Constants.EXTRA_KEYS_ON);
                item.setTitle(R.string.extra_keys_disable);
                setExtraKeysVisibility(View.VISIBLE, false);
                extraKeysHidden = false;
            }
            connection.save(this);
            return true;
        } else if (itemId == R.id.itemHelpInputMode) {
            showDialog(R.id.itemHelpInputMode);
            return true;
        } else {
            boolean inputModeSet = setInputMode(item.getItemId());
            item.setChecked(inputModeSet);
            if (inputModeSet) {
                return inputModeSet;
            }
        }
        return super.onOptionsItemSelected(item);
    }


    public void jieping() {

//        View view = this.getWindow().getDecorView();
//        view.setDrawingCacheEnabled(true);
//        view.buildDrawingCache();
//        Bitmap bitmap = view.getDrawingCache();
        canvas.setDrawingCacheEnabled(true);
        Bitmap bitmap = canvas.getDrawingCache();
        String address = connection.getAddress();
        address = address.replace(".", "");
        int port = connection.getPort();
        String path = getCacheDir().getPath() + "/jieping_" + dId + ".jpg";
        savePNG_After(bitmap, path);
    }

    Bitmap drawable2Bitmap(Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        } else if (drawable instanceof NinePatchDrawable) {
            Bitmap bitmap = Bitmap
                    .createBitmap(
                            drawable.getIntrinsicWidth(),
                            drawable.getIntrinsicHeight(),
                            drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
                                    : Bitmap.Config.RGB_565);
            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
                    drawable.getIntrinsicHeight());
            drawable.draw(canvas);
            return bitmap;
        } else {
            return null;
        }
    }

    public void savePNG_After(Bitmap bitmap, String name) {
        Log.e("XA", "savePNG_After  id = " + dId);
        File file = new File(name);
        try {
            FileOutputStream out = new FileOutputStream(file);
            if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)) {
                out.flush();
                out.close();
            }
            String image64 = Utils.imageToBase64(file.getPath());
            EventBus.getDefault().post(new bVNCEvent.upDeviceImg(image64, dToken, dId));
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public boolean setInputMode(int id) {

        InputHandler input = getInputHandlerById(id);
        if (input != null) {
            Log.e(TAG, "setInputMode id = " + input.getId());
            inputHandler = input;
            connection.setInputMode(input.getId());
            if (input.getId().equals(InputHandlerTouchpad.ID)) {
                connection.setFollowMouse(true);
                connection.setFollowPan(true);
            } else {
                connection.setFollowMouse(false);
                connection.setFollowPan(false);
                canvas.getPointer().setRelativeEvents(false);
            }

            showPanningState(true);
            connection.save(this);
            return true;
        }
        return false;
    }

    private MetaKeyBean lastSentKey;

    private void sendSpecialKeyAgain() {
        if (lastSentKey == null
                || lastSentKey.get_Id() != connection.getLastMetaKeyId()) {
            ArrayList<MetaKeyBean> keys = new ArrayList<MetaKeyBean>();
            Database database = new Database(this);
            Cursor c = database.getReadableDatabase().rawQuery(
                    MessageFormat.format("SELECT * FROM {0} WHERE {1} = {2}",
                            MetaKeyBean.GEN_TABLE_NAME,
                            MetaKeyBean.GEN_FIELD__ID, connection
                                    .getLastMetaKeyId()),
                    MetaKeyDialog.EMPTY_ARGS);
            MetaKeyBean.Gen_populateFromCursor(c, keys, MetaKeyBean.NEW);
            c.close();
            database.close();
            if (keys.size() > 0) {
                lastSentKey = keys.get(0);
            } else {
                lastSentKey = null;
            }
        }
        if (lastSentKey != null)
            canvas.getKeyboard().sendMetaKey(lastSentKey);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (canvas != null)
            canvas.closeConnection();
        System.gc();
        exitFlag = true;
        EventBus.getDefault().unregister(this);
    }


    @Override
    public boolean onKey(View v, int keyCode, KeyEvent evt) {

        boolean consumed = false;

        if (keyCode == KeyEvent.KEYCODE_MENU) {
            if (evt.getAction() == KeyEvent.ACTION_DOWN)
                return super.onKeyDown(keyCode, evt);
            else
                return super.onKeyUp(keyCode, evt);
        }

        try {
            if (evt.getAction() == KeyEvent.ACTION_DOWN || evt.getAction() == KeyEvent.ACTION_MULTIPLE) {
                consumed = inputHandler.onKeyDown(keyCode, evt);
            } else if (evt.getAction() == KeyEvent.ACTION_UP) {
                consumed = inputHandler.onKeyUp(keyCode, evt);
            }
            resetOnScreenKeys(keyCode);
        } catch (NullPointerException e) {
        }

        return consumed;
    }

    public void showPanningState(boolean showLonger) {
        if (showLonger) {
            final Toast t = Toast.makeText(this, inputHandler.getDescription(), Toast.LENGTH_LONG);
            TimerTask tt = new TimerTask() {
                @Override
                public void run() {
                    t.show();
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                    }
                    t.show();
                }
            };
            new Timer().schedule(tt, 2000);
            t.show();
        } else {
            Toast t = Toast.makeText(this, inputHandler.getDescription(), Toast.LENGTH_SHORT);
            t.show();
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see android.app.Activity#onTrackballEvent(android.view.MotionEvent)
     */
    @Override
    public boolean onTrackballEvent(MotionEvent event) {
        try {
            // If we are using the Dpad as arrow keys, don't send the event to the inputHandler.
            if (connection.getUseDpadAsArrows())
                return false;
            return inputHandler.onTouchEvent(event);
        } catch (NullPointerException e) {
        }
        return super.onTrackballEvent(event);
    }

    // Send touch events or mouse events like button clicks to be handled.
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        try {
            return inputHandler.onTouchEvent(event);
        } catch (NullPointerException e) {
        }
        return super.onTouchEvent(event);
    }

    // Send e.g. mouse events like hover and scroll to be handled.
    @Override
    public boolean onGenericMotionEvent(MotionEvent event) {
        // Ignore TOOL_TYPE_FINGER events that come from the touchscreen with HOVER type action
        // which cause pointer jumping trouble in simulated touchpad for some devices.
        boolean toolTypeFinger = false;
        if (Constants.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            toolTypeFinger = event.getToolType(0) == MotionEvent.TOOL_TYPE_FINGER;
        }
        int a = event.getAction();
        if (!((a == MotionEvent.ACTION_HOVER_ENTER ||
                a == MotionEvent.ACTION_HOVER_EXIT ||
                a == MotionEvent.ACTION_HOVER_MOVE) &&
                event.getSource() == InputDevice.SOURCE_TOUCHSCREEN &&
                toolTypeFinger
        )) {
            try {
                return inputHandler.onTouchEvent(event);
            } catch (NullPointerException e) {
            }
        }
        return super.onGenericMotionEvent(event);
    }

    private void selectColorModel() {

        String[] choices = new String[COLORMODEL.values().length];
        int currentSelection = -1;
        for (int i = 0; i < choices.length; i++) {
            COLORMODEL cm = COLORMODEL.values()[i];
            choices[i] = cm.toString();
            if (canvas.isColorModel(cm))
                currentSelection = i;
        }

        final Dialog dialog = new Dialog(this);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        ListView list = new ListView(this);
        list.setAdapter(new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_checked, choices));
        list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        list.setItemChecked(currentSelection, true);
        list.setOnItemClickListener(new OnItemClickListener() {
            public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
                dialog.dismiss();
                COLORMODEL cm = COLORMODEL.values()[arg2];
                canvas.setColorModel(cm);
                connection.setColorModel(cm.nameString());
                connection.save(RemoteCanvasActivity.this);
                Toast.makeText(RemoteCanvasActivity.this, getString(R.string.info_update_color_model_to) + cm.toString(), Toast.LENGTH_SHORT).show();
            }
        });
        dialog.setContentView(list);
        dialog.show();
    }

    final long hideToolbarDelay = 2500;
    ToolbarHiderRunnable toolbarHider = new ToolbarHiderRunnable();

    public void showToolbar() {
        if (!softKeyboardUp) {
//            getSupportActionBar().show();
            handler.removeCallbacks(toolbarHider);
            handler.postAtTime(toolbarHider, SystemClock.uptimeMillis() + hideToolbarDelay);
        }
    }

    @Override
    public void onTextObtained(String id, String obtainedString) {
        android.util.Log.i(TAG, "onTextObtained called with id: " + id);
        canvas.pd.show();

        if (id.equals(RemoteClientLibConstants.GET_PASSWORD_ID)) {
            connection.setPassword(obtainedString);
        } else if (id.equals(RemoteClientLibConstants.GET_OTP_CODE_ID)) {
            connection.setOtpCode(obtainedString);
        }

        synchronized (canvas.spicecomm) {
            canvas.spicecomm.notify();
        }

//        FragmentManager fm = this.getSupportFragmentManager();
//        fm.beginTransaction().remove(fm.findFragmentByTag(id)).commit();
    }

    @Override
    public void onTextSelected(String selectedString) {
        android.util.Log.i(TAG, "onTextSelected called with selectedString: " + selectedString);
        canvas.pd.show();
        connection.setVmname(canvas.vmNameToId.get(selectedString));
        connection.save(this);
        synchronized (canvas.spicecomm) {
            canvas.spicecomm.notify();
        }
    }

    private class ToolbarHiderRunnable implements Runnable {
        public void run() {
//            ActionBar toolbar = getSupportActionBar();
//            if (toolbar != null)
//                toolbar.hide();
        }
    }

    public void showKeyboard(MenuItem menuItem) {
        android.util.Log.i(TAG, "Showing keyboard and hiding action bar");
        InputMethodManager inputMgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        canvas.requestFocus();
        inputMgr.showSoftInput(canvas, 0);
        softKeyboardUp = true;
//        getSupportActionBar().hide();
    }

    public Connection getConnection() {
        return connection;
    }


    public RemoteCanvas getCanvas() {
        return canvas;
    }


    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
        super.onSaveInstanceState(outState);
    }

    private boolean isMasterPasswordEnabled() {
        SharedPreferences sp = getSharedPreferences(Constants.generalSettingsTag, Context.MODE_PRIVATE);
        return sp.getBoolean(Constants.masterPasswordEnabledTag, false);
    }

    @Override
    public void onBackPressed() {
        inputHandler.onKeyDown(KeyEvent.KEYCODE_BACK, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
    }

    @Subscribe
    public void handleEvent(bVNCEvent.closeRemote event) {
        disConnect();
    }

    @Subscribe
    public void handleEvent(bVNCEvent.closeRemoteAndOpen event) {
        Log.e("XA", "event closeRemoteAndOpen ip = " + event.ip + "  port : " + event.port);
        disConnectAndOpen(event.ip, event.port, event.token, event.id);
    }


}
